//
// Created by twetec on 25-8-16.
//

#include "../include/network_prober/probe_detect/handle_mysql_probe_detect.h"

#include <boost/json.hpp>
#include <mysql/mysql.h>
#include <future>

using namespace std::literals::chrono_literals;

namespace network_prober::probe_detect
{
void tag_invoke(boost::json::value_from_tag, boost::json::value &jv, MySQLProbeDetectResultIPv4 const &c)
{
    auto& ja = jv.emplace_object();

    ja["interface"] = c.interface;
    ja["target"] = c.target;
    ja["port"] = c.port;
    ja["delay"] = c.delay;
}

HandleMySQLProbeDetectIPv4::HandleMySQLProbeDetectIPv4(
    std::string_view interface,
    std::string_view target,
    std::uint32_t port,
    std::string_view userName,
    std::string_view password,
    std::string_view databaseName,
    std::string_view queryStatement
)
    : m_interface(interface), m_target(target), m_port(port), m_userName(userName),
    m_password(password), m_databaseName(databaseName), m_queryStatement(queryStatement)
{
}

void HandleMySQLProbeDetectIPv4::detect()
{
    // 建立连接时，开始计时
    m_detectStartTime = std::chrono::steady_clock::now();

    // 指向当前对象的shared_ptr
    auto self{shared_from_this()};

    // 发起探测
    m_onDetectedCallbackFuture = std::async(std::launch::async, [this, self] {
        // 探测结果
        MySQLProbeDetectResultIPv4 result;
        result.interface = m_interface;
        result.target = m_target;
        result.port = m_port;

        // 初始化数据库连接
        MYSQL* connPtr = mysql_init(nullptr);
        // 如果初始化失败，则设置延迟为-1
        if (!connPtr) {
            // 错误信息
            SPDLOG_INFO("MySQL观测目标连接初始化失败：{}", mysql_error(connPtr));
            // 关闭连接
            mysql_close(connPtr);
            // 连接失败，延迟为-1
            result.delay = -1;
            // 回调结果
            m_onDetectedCallback(std::move(result));
            // 结束后续执行
            return;
        }

        // 设置字符编码
        mysql_options(connPtr, MYSQL_SET_CHARSET_NAME, "utf8");
        // 设置客户端网口
        mysql_options(connPtr, MYSQL_OPT_BIND, m_interface.c_str());
        // 设置自动重连
        mysql_options(connPtr, MYSQL_OPT_CONNECT_TIMEOUT, "5");

        // 连接数据库 如果连接失败，则设置延迟为-1
        if (mysql_real_connect(connPtr, m_target.c_str(), m_userName.c_str(), m_password.c_str(), m_databaseName.c_str(), m_port, NULL, 0) == NULL) {
            // 错误信息
            SPDLOG_INFO("MySQL观测目标连接失败：{}", mysql_error(connPtr));
            // 连接失败，延迟为-1
            result.delay = -1;
            // 回调结果
            m_onDetectedCallback(std::move(result));
            // 关闭连接
            mysql_close(connPtr);
            // 结束后续执行
            return;
        }

        // 构建SQL语句
        const char* sql = m_queryStatement.empty()? "SELECT 1" : m_queryStatement.c_str();

        // 发送查询语句，如果查询结果出错，则延迟为-1
        if (mysql_query(connPtr, sql) != 0) {
            // 错误信息
            SPDLOG_INFO("MySQL观测目标查询失败：{}", mysql_error(connPtr));
            // 关闭连接
            mysql_close(connPtr);
            // 连接失败，延迟为-1
            result.delay = -1;
            // 回调结果
            m_onDetectedCallback(std::move(result));
            // 结束后续执行
            return;
        }

        // 关闭连接
        mysql_close(connPtr);

        // SQL正常执行完毕,计算延迟
        result.delay = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - m_detectStartTime).count();
        // 回调结果
        m_onDetectedCallback(std::move(result));
    });
}

void HandleMySQLProbeDetectIPv4::setOnDetectedCallback(std::function<void(MySQLProbeDetectResultIPv4&&)> callback)
{
    m_onDetectedCallback = std::move(callback);
}

HandleMySQLProbeDetectIPv4::~HandleMySQLProbeDetectIPv4()
{

}

void tag_invoke(boost::json::value_from_tag, boost::json::value &jv, MySQLProbeDetectResultIPv6 const &c)
{
    auto& ja = jv.emplace_object();

    ja["interface"] = c.interface;
    ja["target"] = c.target;
    ja["port"] = c.port;
    ja["delay"] = c.delay;
}

HandleMySQLProbeDetectIPv6::HandleMySQLProbeDetectIPv6(
    std::string_view interface,
    std::string_view target,
    std::uint32_t port,
    std::string_view userName,
    std::string_view password,
    std::string_view databaseName,
    std::string_view queryStatement
)
    : m_interface(interface), m_target(target), m_port(port), m_userName(userName),
    m_password(password), m_databaseName(databaseName), m_queryStatement(queryStatement)
{
}

void HandleMySQLProbeDetectIPv6::detect()
{
    // 建立连接时，开始计时
    m_detectStartTime = std::chrono::steady_clock::now();

    // 指向当前对象的shared_ptr
    auto self{shared_from_this()};

    // 发起探测
    m_onDetectedCallbackFuture = std::async(std::launch::async, [this, self] {
        // 探测结果
        MySQLProbeDetectResultIPv6 result;
        result.interface = m_interface;
        result.target = m_target;
        result.port = m_port;

        // 初始化数据库连接
        MYSQL* connPtr = mysql_init(nullptr);
        // 如果初始化失败，则设置延迟为-1
        if (!connPtr) {
            // 错误信息
            SPDLOG_INFO("MySQL观测目标连接初始化失败：{}", mysql_error(connPtr));
            // 关闭连接
            mysql_close(connPtr);
            // 连接失败，延迟为-1
            result.delay = -1;
            // 回调结果
            m_onDetectedCallback(std::move(result));
            // 结束后续执行
            return;
        }

        // 设置字符编码
        mysql_options(connPtr, MYSQL_SET_CHARSET_NAME, "utf8");
        // 设置客户端网口
        // mysql_options(connPtr, MYSQL_OPT_BIND, m_interface.c_str());
        // 设置自动重连
        mysql_options(connPtr, MYSQL_OPT_CONNECT_TIMEOUT, "5");

        // 连接数据库 如果连接失败，则设置延迟为-1
        if (mysql_real_connect(connPtr, m_target.c_str(), m_userName.c_str(), m_password.c_str(), m_databaseName.c_str(), m_port, NULL, 0) == NULL) {
            // 错误信息
            SPDLOG_INFO("MySQL观测目标连接失败：{}", mysql_error(connPtr));
            // 连接失败，延迟为-1
            result.delay = -1;
            // 回调结果
            m_onDetectedCallback(std::move(result));
            // 关闭连接
            mysql_close(connPtr);
            // 结束后续执行
            return;
        }

        // 构建SQL语句
        const char* sql = m_queryStatement.empty()? "SELECT 1" : m_queryStatement.c_str();

        // 发送查询语句，如果查询结果出错，则延迟为-1
        if (mysql_query(connPtr, sql) != 0) {
            // 错误信息
            SPDLOG_INFO("MySQL观测目标查询失败：{}", mysql_error(connPtr));
            // 关闭连接
            mysql_close(connPtr);
            // 连接失败，延迟为-1
            result.delay = -1;
            // 回调结果
            m_onDetectedCallback(std::move(result));
            // 结束后续执行
            return;
        }

        // 关闭连接
        mysql_close(connPtr);

        // SQL正常执行完毕,计算延迟
        result.delay = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - m_detectStartTime).count();
        // 回调结果
        m_onDetectedCallback(std::move(result));
    });
}

void HandleMySQLProbeDetectIPv6::setOnDetectedCallback(std::function<void(MySQLProbeDetectResultIPv6&&)> callback)
{
    m_onDetectedCallback = std::move(callback);
}

HandleMySQLProbeDetectIPv6::~HandleMySQLProbeDetectIPv6()
{

}

}